﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Liang.Util.Crypt.Dll;

namespace Liang.Util.Crypt
{
    /// <summary>
    /// 电子票加解密类
    /// <para>本类加解密依赖Advapi32.dll</para>
    /// <para>请勿用于其他项目，本来不久将来将被废弃</para>
    /// </summary>
    public class ElectronicBillCrypt : ICrypt
    {
        /// <summary>
        /// 根据口令加密字符串，返回加密后的字符串
        /// </summary>
        /// <param name="strData"></param>
        /// <param name="strKey"></param>
        /// <returns></returns>
        public string EncStr(string strData, string strKey)
        {
            byte[] inBuf = Encoding.ASCII.GetBytes(strData);
            byte[] outBuf = new byte[inBuf.Length];
            inBuf.CopyTo(outBuf, 0);

            string strRet = "";
            if (Encry(ref outBuf, strKey))
            {
                strRet = Convert.ToBase64String(outBuf);
            }

            return strRet;
        }

        /// <summary>
        /// 根据口令将加密并编码后的十六进制字符串解密，返回原字符串
        /// </summary>
        /// <param name="strData"></param>
        /// <param name="strKey"></param>
        /// <returns></returns>
        public string DecStr(string strData, string strKey)
        {
            byte[] buf = Convert.FromBase64String(strData);

            string strRtn = "";
            if (Decry(ref buf, strKey))
            {
                strRtn = Encoding.ASCII.GetString(buf);
            }

            return strRtn;
        }

        /// <summary>
        /// 对称加密，必须用Decry解密
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool Encry(ref byte[] buf, string keys)
        {
            // CSP句柄
            IntPtr hProv = IntPtr.Zero;
            // 密钥
            IntPtr hKey = IntPtr.Zero;
            // 哈希句柄
            IntPtr hHash = IntPtr.Zero;

            //取得CSP服务
            if (0 == CryptAPI.CryptAcquireContext(
                ref hProv,
                null,
                Model.CSPProvider.MS_ENHANCED_PROV,
                Model.CSPProviderType.PROV_RSA_FULL,
                0))
            {
                // 修改参数再次调用
                if (0 == CryptAPI.CryptAcquireContext(
                    ref hProv,
                    null,
                    Model.CSPProvider.MS_ENHANCED_PROV,
                    Model.CSPProviderType.PROV_RSA_FULL,
                    Model.CryptAcquireContextFlags.CRYPT_NEWKEYSET))
                {
                    return false;
                }
            }

            bool bSuccessed = false;
            // 创建哈希句柄
            if (0 != CryptAPI.CryptCreateHash((uint)hProv, Model.AlgID.CALG_SHA, 0, 0, ref hHash))
            {
                // 将key散列
                byte[] pbData = Encoding.Unicode.GetBytes(keys);
                if (0 != CryptAPI.CryptHashData((uint)hHash, pbData, (uint)pbData.Length, 0))
                {
                    // 派生密钥
                    if (0 != CryptAPI.CryptDeriveKey((uint)hProv, Model.AlgID.CALG_RC4, (uint)hHash, 0, ref hKey))
                    {
                        // 计算需要的密码存储缓冲长度
                        IntPtr tmp = IntPtr.Zero;
                        // 待加密的数据长度
                        uint nLen = (uint)buf.Length;
                        uint dwCount = nLen;
                        if (0 != CryptAPI.CryptEncrypt((uint)hKey, 0, 1, 0, tmp, ref dwCount, 0))
                        {
                            // 加密
                            byte[] pbBuffer = new byte[dwCount];
                            buf.CopyTo(pbBuffer, 0);

                            GCHandle hObject = GCHandle.Alloc(pbBuffer, GCHandleType.Pinned);

                            if (hObject.IsAllocated)
                            {
                                IntPtr hBuffer = hObject.AddrOfPinnedObject();
                                if (0 != CryptAPI.CryptEncrypt((uint)hKey, 0, 1, 0, hBuffer, ref dwCount, nLen))
                                {
                                    pbBuffer.CopyTo(buf, 0);
                                    bSuccessed = true;
                                }

                                hObject.Free();
                            }
                        }
                    }
                }
            }

            if (IntPtr.Zero != hKey)
            {
                CryptAPI.CryptDestroyKey((uint)hKey);
            }

            if (IntPtr.Zero != hHash)
            {
                CryptAPI.CryptDestroyHash((uint)hHash);
            }

            if (IntPtr.Zero != hProv)
            {
                CryptAPI.CryptReleaseContext((uint)hProv, 0);
            }

            return bSuccessed;
        }

        /// <summary>
        /// 对称解密，对应于Encry
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="keys"></param>
        /// <returns></returns>
        public bool Decry(ref byte[] buf, string keys)
        {
            // CSP句柄
            IntPtr hProv = IntPtr.Zero;
            // 密钥
            IntPtr hKey = IntPtr.Zero;
            // 哈希句柄
            IntPtr hHash = IntPtr.Zero;

            // 取得CSP服务
            if (0 == CryptAPI.CryptAcquireContext(
                ref hProv,
                null,
                Model.CSPProvider.MS_ENHANCED_PROV,
                Model.CSPProviderType.PROV_RSA_FULL,
                0))
            {
                // 修改参数再次调用
                if (0 == CryptAPI.CryptAcquireContext(
                    ref hProv,
                    null,
                    Model.CSPProvider.MS_ENHANCED_PROV,
                    Model.CSPProviderType.PROV_RSA_FULL,
                    Model.CryptAcquireContextFlags.CRYPT_NEWKEYSET))
                {
                    return false;
                }
            }

            // 创建哈希
            if (0 != CryptAPI.CryptCreateHash((uint)hProv, Model.AlgID.CALG_SHA, 0, 0, ref hHash))
            {
                // 散列key
                byte[] pbData = Encoding.Unicode.GetBytes(keys);
                if (0 != CryptAPI.CryptHashData((uint)hHash, pbData, (uint)pbData.Length, 0))
                {
                    // 派生密钥
                    if (0 != CryptAPI.CryptDeriveKey((uint)hProv, Model.AlgID.CALG_RC4, (uint)hHash, 0, ref hKey))
                    {
                        // 解密
                        uint dwCount = (uint)buf.Length;
                        byte[] pbBuffer = new byte[dwCount];
                        buf.CopyTo(pbBuffer, 0);

                        GCHandle hObject = GCHandle.Alloc(pbBuffer, GCHandleType.Pinned);

                        if (hObject.IsAllocated)
                        {
                            IntPtr hBuffer = hObject.AddrOfPinnedObject();
                            if (0 != CryptAPI.CryptDecrypt((uint)hKey, 0, 1, 0, hBuffer, ref dwCount))
                            {
                                pbBuffer.CopyTo(buf, 0);
                            }

                            hObject.Free();
                        }
                    }
                }
            }

            if (IntPtr.Zero != hKey)
            {
                CryptAPI.CryptDestroyKey((uint)hKey);
            }

            if (IntPtr.Zero != hHash)
            {
                CryptAPI.CryptDestroyHash((uint)hHash);
            }

            if (IntPtr.Zero != hProv)
            {
                CryptAPI.CryptReleaseContext((uint)hProv, 0);
            }

            return true;
        }
    }
}